package cn.tedu.springboot.security.filter;

import cn.tedu.springboot.security.sety.LoginPrincipal;
import cn.tedu.springboot.security.web.JsonResult;
import cn.tedu.springboot.security.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * @author BJ
 * @date 2022/10/22 16:38
 * jwt 过滤认证
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${csmall.jwt.secret-key}")
    String secretKey;

    public static final int JWT_MIN_LENGTH = 100;

    private void TipContextTOJSON(HttpServletResponse response, ServiceCode serviceCode, String message) throws IOException {
        JsonResult<Void> jsonResult = JsonResult.fail(serviceCode, message);
        String jsonResultString  = JSON.toJSONString(jsonResult);
        PrintWriter writer = response.getWriter();
        writer.println(jsonResultString);
        writer.close();
        return;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        //将 SecurityContext中 的原有数据(认证信息)清除掉(不是必要的)
        SecurityContextHolder.clearContext();

        // 准备 解析 jwt  ;"Authorization"为行内默认的
        String jwt = request.getHeader("Authorization");
        log.debug("接收到客户端携带的jwt:{}",jwt);

        if (!StringUtils.hasText(jwt) || jwt.length() < JWT_MIN_LENGTH) {
            log.debug("未获取到有效的JWT数据，将直接放行");
            filterChain.doFilter(request,response);
            return;
        }

        //设置响应的文档类型(json格式)
        response.setContentType("application/json;charset=utf-8");

        // 尝试解析JWT，从中获取用户的相关数据，例如id、username等
        log.debug("将尝试解析jwt...");
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        } catch (SignatureException e) {
            TipContextTOJSON(response, ServiceCode.ERR_JWT_SIGNATURE,"非法访问!");
        } catch (ExpiredJwtException e) {
            TipContextTOJSON(response,ServiceCode.ERR_JWT_EXPIRED,"登录已过期,请重新登录!!");
        } catch (MalformedJwtException e) {
            TipContextTOJSON(response,ServiceCode.ERR_JWT_MALFORMED,"非法访问!");
        } catch (IllegalArgumentException e) {
            TipContextTOJSON(response,ServiceCode.ERR_JWT_ILLEGAL,"非法访问!");
        } catch (Throwable e){
            e.printStackTrace();
            TipContextTOJSON(response,ServiceCode.ERR_UNKNOWN,"未知异常!");
        }

        // 从中获取用户的相关信息
        Long id = claims.get("id", Long.class);
        String username = claims.get("username", String.class);
        String authoritiesJsonString = claims.get("authorities", String.class);

        // 准备用于创建认证信息的权限信息(controller层可以配置当前可以访问的权限[不设置则表示所有登录用户权限一样])
        final List<SimpleGrantedAuthority> authorities
                = JSON.parseArray(authoritiesJsonString, SimpleGrantedAuthority.class);

        //准备用于创建认证信息的当事人信息(主要是在controller层 配置,显示当前登录的用户[按照业务需求是否添加])
        LoginPrincipal loginPrincipal = new LoginPrincipal();
        loginPrincipal.setId(id);
        loginPrincipal.setUsername(username);

        // 注意：以下调用构造方法时，第1个参数是以上创建的对象
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                loginPrincipal, null, authorities);

        //将认证信息存储到SecurityContext中
        SecurityContextHolder.getContext().setAuthentication(authentication);

        //放行
        filterChain.doFilter(request,response);
    }
}
